namespace JanusLibAing.Threading
{
    using System.ComponentModel;
    using System.Globalization;
    using System.Threading;
    using Conditions;
    using EventsArg;

    public abstract class ExecutionAsync : IExecutionAsync
    {
        private BackgroundWorker worker = new BackgroundWorker();

        private DefaultExitCondition.EExitCode exitCode = DefaultExitCondition.EExitCode.EXIT_OK;

        private IActionNotification actionNotification = null;
        private int actionId = 0;

        private ManualResetEvent finishWaiter = new ManualResetEvent(false);
        private bool started = false;
        public event RunWorkerCompletedEventHandler OnRunWorkerCompletedEvent = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="ExecutionAsync"/> class.
        /// </summary>
        /// <param name="actionId">The action id.</param>
        /// <param name="actionNotification">The action notification.</param>
        protected ExecutionAsync(int actionId, IActionNotification actionNotification)
        {
            this.actionId = actionId;
            this.actionNotification = actionNotification;
            if (worker != null)
            {
                worker.WorkerSupportsCancellation = true;
            }
        }

        /// <summary>
        /// Updates the action notification.
        /// </summary>
        /// <param name="notification">The notification.</param>
        public void UpdateActionNotification(IActionNotification notification)
        {
            if (!started)
            {
                this.actionNotification = notification;
            }
        }

        /// <summary>
        /// Start execution
        /// </summary>
        public virtual void Start()
        {
            CultureInfo currentUICulture = Thread.CurrentThread.CurrentUICulture;
            if (worker == null)
            {
                worker = new BackgroundWorker();
                worker.WorkerSupportsCancellation = true;
            }

            if (worker != null && !worker.IsBusy)
            {
                worker.DoWork += worker_DoWork;
                worker.RunWorkerCompleted += worker_RunWorkerCompleted;
#if EA_TRACE
                Trace.WriteLine(string.Format("EA :Start execution {0}", this)); //MLHIDE
#endif
                started = true;
                LanguageHelper.StartThread(worker);
            }
        }

        /// <summary>
        /// Handles the DoWork event of the worker control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void worker_DoWork(object sender, DoWorkEventArgs e)
        {
#if EA_TRACE
            Trace.WriteLine(string.Format("EA :worker_DoWork start {0}", this)); //MLHIDE
#endif
            //try // we don't need it it is already done in BackgroundWorker class
            LanguageHelper.InitThread(e);
#if TRACE_SLU
#else
			Thread.CurrentThread.Name = this.ToString();
#endif

            if (ActionNotification != null)
            {
                ActionEventArgs arg = new ActionEventArgs(ActionId);
                ActionNotification.Started(this, arg);
            }

            DoWork(sender, e);

#if EA_TRACE
            Trace.WriteLine(string.Format("EA :worker_DoWork stop {0}", this)); //MLHIDE
#endif
        }

        /// <summary>
        /// Handles the RunWorkerCompleted event of the worker control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            RunWorkerCompleted(sender, e);
            started = false;
//#if EA_TRACE
//            Trace.WriteLine(string.Format("EA :worker_RunWorkerCompleted {0}", this)); //MLHIDE
//#endif
            finishWaiter.Set();
        }

        /// <summary>
        /// Invokes the on run worker completed event.
        /// </summary>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void InvokeOnRunWorkerCompletedEvent(RunWorkerCompletedEventArgs e)
        {
            RunWorkerCompletedEventHandler eventFunct = OnRunWorkerCompletedEvent;
            if (eventFunct != null)
            {
                eventFunct(this, e);
            }
        }

        /// <summary>
        /// Determines whether [is on run worker completed event assigned].
        /// </summary>
        /// <returns>
        ///   <c>true</c> if [is on run worker completed event assigned]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsOnRunWorkerCompletedEventAssigned()
        {
            return OnRunWorkerCompletedEvent != null;
        }

        /// <summary>
        /// Wait thread finishing.
        /// Waiting could be endless
        /// </summary>
        public void WaitFinishing()
        {
            WaitFinishing(0);
        }

        /// <summary>
        /// Wait up to thread is finished
        /// </summary>
        /// <param name="timeout">if bigger 0 then waiting is stopped after this time in milliseconds</param>
        public void WaitFinishing(int timeout)
        {
            if (timeout > 0)
            {
                finishWaiter.WaitOne(timeout, false);
            }
            else
            {
                finishWaiter.WaitOne();
            }
        }

        /// <summary>
        /// Close action.
        /// Active thread will be aborted async
        /// </summary>
        public virtual void Close()
        {
            if (worker != null)
            {
                worker.CancelAsync();
            }

            Thread.Sleep(10);

            // wait not more as 300ms to thread finishing
            for (int i = 0; i < 30; i++)
            {
                if (worker != null && !worker.IsBusy)
                {
                    break;
                }
                Thread.Sleep(10);
            }
        }

        /// <summary>
        /// Closes the specified e.
        /// </summary>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        protected virtual void Close(RunWorkerCompletedEventArgs e)
        {
            if (worker != null)
            {
                worker.DoWork -= DoWork;
                worker.RunWorkerCompleted -= worker_RunWorkerCompleted;
            }

            worker = null;

            if (ActionNotification != null)
            {
                ExecutionCompletedEventArgs arg = null;
                if (e != null)
                {
                    arg = new ExecutionCompletedEventArgs(ActionId, exitCode, e.Error, e.Cancelled);
                }
                else
                {
                    arg = new ExecutionCompletedEventArgs(ActionId, exitCode, null, false);
                }

                ActionNotification.Finished(this, arg);
            }
        }

        /// <summary>
        /// Occurs when the background operation has completed, has been canceled, or has raised an exception.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        public virtual void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
#if EA_TRACE
            Trace.WriteLine(string.Format("EA :RunWorkerCompleted {0}", this)); //MLHIDE
#endif
            InvokeOnRunWorkerCompletedEvent(e);
            Close(e);
        }
        
        /// <summary>
        /// Does the work.
        /// Occurs when Start is called.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        public abstract void DoWork(object sender, DoWorkEventArgs e);

        /// <summary>
        /// Gets a value indicating whether this instance is active.
        /// </summary>
        /// <value><c>true</c> if this instance is active; otherwise, <c>false</c>.</value>
        public bool IsActive
        {
            get
            {
                return worker.IsBusy;
            }
        }

        /// <summary>
        /// Query to cancel operation
        /// Operation could not be cancelled immediately
        /// </summary>
        public virtual void CancelAsync()
        {
            if (worker != null)
            {
                worker.CancelAsync();
            }
        }

        /// <summary>
        /// Gets a value indicating whether this action instance could be executed.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance can execute; otherwise, <c>false</c>.
        /// </value>
        public virtual bool CanExecute
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance can execute next action.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance can execute next; otherwise, <c>false</c>.
        /// </value>
        public virtual bool CanExecuteNext
        {
            get
            {
                return ExitCode == DefaultExitCondition.EExitCode.EXIT_OK;
            }
        }

        /// <summary>
        /// Gets a value indicating whether [cancellation pending].
        /// </summary>
        /// <value><c>true</c> if [cancellation pending]; otherwise, <c>false</c>.</value>
        public bool CancellationPending
        {
            get
            {
                bool ret = false;
                if (worker != null)
                {
                    ret = worker.CancellationPending;
                }

                return ret;
            }
        }

        /// <summary>
        /// Gets or sets the exit code.
        /// </summary>
        /// <value>The exit code.</value>
        public DefaultExitCondition.EExitCode ExitCode
        {
            get
            {
                return exitCode;
            }

            protected set
            {
                exitCode = value;
            }
        }

        /// <summary>
        /// Gets the action notification.
        /// </summary>
        /// <value>The action notification.</value>
        public IActionNotification ActionNotification
        {
            get
            {
                return actionNotification;
            }

            private set
            {
                actionNotification = value;
            }
        }

        /// <summary>
        /// Gets the action id.
        /// </summary>
        /// <value>The action id.</value>
        public int ActionId
        {
            get
            {
                return actionId;
            }
        }
    }
}